/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Effect.cpp
*
*	Comments	-	See Effect.h
*
**************************************************************************************/
#include "../Include/Effect.h"
#include "../Include/XMLParser.h"
#include "../Include/GraphicsResourceManager.h"
#include "../Include/GeometryElementInfo.h"
#include "../Include/MaterialElementInfo.h"

namespace Pulse
{
	Effect::Effect( void )
		: m_pTechnique( PSX_NULL ), m_techniquePriority( 0 )
	{
		
	}

	Effect::~Effect( void )
	{
		Cleanup();
	}

	BOOL Effect::CreateResource( BaseResDesc *pDesc )	
	{
		if ( pDesc->GetType() != GetType() )
			return FALSE;

		m_desc = *((EffectDesc*)pDesc);

		if ( PerformInitialize( m_desc.IsAsyncLoad() ) == EErrorCode::OKAY )
			SetFlagCreated();

		return IsCreated();	
	}

	BOOL Effect::DestroyResource( void )				
	{
		UnloadResource();
		SetFlagDestroyed();	
		return TRUE;	
	}

	BOOL Effect::LoadResource( void )				
	{ 
		if ( Initialize() == EErrorCode::OKAY )
		{
			SetFlagRestored();
			return TRUE;
		}

		return FALSE;	
	}

	BOOL Effect::UnloadResource( void )				
	{
		Cleanup();
		SetFlagDisabled();
		return TRUE;	
	}

	EErrorCode::Type Effect::Initialize( void )
	{
		if ( m_desc.m_filename.IsEmpty() )
		{
			PSX_PushError( "No filename specified." );
			return EErrorCode::INVALID_PARAMETER;
		}

		XMLDocumentPtr pDoc = XMLParser::Load( (PSX_PATH_EFFECTS + m_desc.m_filename).GetBuffer() );

		if ( pDoc.IsNull() )
		{
			PSX_PushError( "Failed to load effect file." );
			return EErrorCode::_ERROR;
		}

		XMLElementPtr pElem = pDoc->FindChild(PSX_String("Effect"));

		if ( pElem.IsNull() )
		{
			PSX_PushError( "Missing Effect info." );
			return EErrorCode::INVALID_FORMAT;
		}

		EErrorCode::Type retCode = ProcessEffect( pElem );

		if ( retCode != EErrorCode::OKAY )
		{
			PSX_PushError( "Failed to load effect.");
			return EErrorCode::_ERROR;
		}

		return EErrorCode::OKAY;
	}

	void Effect::Cleanup( void )
	{
		//ClearRenderPasses();
		if ( m_pTechnique )
			m_pTechnique->Release();


	}

	void Effect::RegisterTechnique( RenderTechnique *pTech, TechniquePriority priority )
	{
		if ( m_pTechnique )
		{
			// We always choose the technique with a higher priority.
			// If priority is lower than the current one, simply skip.
			if ( priority < m_techniquePriority )
			{
				return;
			}

			m_pTechnique->Release();
		}

		m_pTechnique = pTech;
		m_techniquePriority = priority;

		if ( m_pTechnique )
			m_pTechnique->AddRef();
	}

	RenderTechnique * Effect::GetTechnique( void )
	{
		if ( m_pTechnique )
		{
			m_pTechnique->AddRef();
			return m_pTechnique;
		}

		return PSX_NULL;
	}

	EErrorCode::Type Effect::ProcessEffect( XMLElementPtr pElem )
	{
		EErrorCode::Type retCode;
		XMLElementPtr pChild = pElem->FindChild( PSX_String("Info") );

		if ( pElem.IsValid() )
		{
			retCode = ProcessInfo( pChild );
		
			if ( retCode != EErrorCode::OKAY )
				return EErrorCode::_ERROR;
		}

		pChild = pElem->FindChild( PSX_String("Requirements") );

		if ( pElem.IsNull() )
		{
			PSX_PushError( "Missing Requirements info." );
			return EErrorCode::INVALID_FORMAT;
		}

		retCode = ProcessRequirements( pChild );

		if ( retCode != EErrorCode::OKAY )
			return EErrorCode::_ERROR;

		return EErrorCode::OKAY;
	}

	EErrorCode::Type Effect::ProcessInfo( XMLElementPtr pElem )
	{
		m_desc.m_name = pElem->FindChild( PSX_String("Name") )->GetValue();
		m_desc.m_description = pElem->FindChild( PSX_String("Description") )->GetValue();
		return EErrorCode::OKAY;
	}

	EErrorCode::Type Effect::ProcessRequirements( XMLElementPtr pElem )
	{
		// The requirements section is split into two parts: Geometry and Material

		XMLElementPtr pChild = pElem->FindChild( PSX_String("Geometry") );

		if ( pChild.IsNull() )
		{
			PSX_PushError( "Missing Geometry requirements." );
			return EErrorCode::INVALID_PARAMETER;
		}

		{
			XMLElementPtr pGeometryElement = pChild->GetFirstChild();

			while ( pGeometryElement.IsValid() )
			{
				if ( pGeometryElement->GetName() == PSX_String("Element") )
				{
					GeometryElementRequirement requirement;

					GeometryElementInfo::GetRequirementElement( pGeometryElement, &requirement );
			
					if ( requirement.m_type != EVertexElement::UNKNOWN )
						m_geometryRequirements.PushBack( requirement );
					else
						PSX_PushWarning( "Skipping. Invalid vertex element type." );
				}

				pGeometryElement = pGeometryElement->GetNextSibling();
			}
		}

		pChild = pElem->FindChild( PSX_String("Material") );

		if ( pChild.IsNull() )
		{
			PSX_PushError( "Missing Material requirements." );
			return EErrorCode::INVALID_FORMAT;
		}

		{
			XMLElementPtr pMaterialRequirement = pChild->GetFirstChild();

			while ( pMaterialRequirement.IsValid() )
			{
				if ( pMaterialRequirement->GetName() == PSX_String("Element") )
				{
					MaterialElementRequirement requirement;

					MaterialElementInfo::GetRequirementElement( pMaterialRequirement, &requirement );

					if ( requirement.m_type != EParameter::UNKNOWN )
						m_materialRequirements.PushBack( requirement );
					else
					{
						PSX_PushWarning( "Skipping. Invalid material element type." );
					}
				}

				pMaterialRequirement = pMaterialRequirement->GetNextSibling();
			}
		}

		return EErrorCode::OKAY;
	}

	GeometryElementRequirement * Effect::GetGeometryRequirements( void )
	{
		if ( m_geometryRequirements.GetSize() )
			return &m_geometryRequirements[0];
		else
			return PSX_NULL;
	}
	
	MaterialElementRequirement * Effect::GetMaterialRequirements( void )
	{
		if ( m_materialRequirements.GetSize() )
			return &m_materialRequirements[0]; 
		else
			return PSX_NULL;
	}


}